////////////////////////////////////////////////////////////////////////////////
// Copyright 2006 CORE Solutions Limited
// All rights reserved.
// PROPRIETARY AND CONFIDENTIAL INFORMATION.
// Use is subject to license terms.
//
// FUNCTION ID      :   BeanQuery.java
// AUTHOR           :   ric.yik
// DATE CREATE      :   2006/5/13
// DESCRIPTION      :   
//                  :   
//                  :   
//                  :   
//
// MODIFICATION HISTORY:
// DATE         AUTHOR          VERSION NO.         COMMENT
// -----------  --------------  ------------------  -------------------
// 2006/5/13      ric.yik         1.0                 source is created.
//
//
//
////////////////////////////////////////////////////////////////////////////////

package com.erp.app.common.query;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import com.erp.framework.SimpleConstants;
import com.erp.framework.util.BeanUtil;
import com.erp.framework.util.CommonUtil;

public class BeanQuery extends SimpleQueryObject implements Expression {

    private Object model;

    protected abstract class SimpleExpression implements Expression {
        private final String expression;

        public SimpleExpression(String expression) {
            super();
            this.expression = expression;
        }

        public String getExpression() {
            return expression;
        }

    }

    protected class BeanPropertyExpression extends SimpleExpression {

        private static final String DB_NULL = "NULL";

        protected static final String TYPE_START_TOKEN = ":";

        private String property;

        private String type;

        public BeanPropertyExpression(String expression) {
            super(expression);
            this.parse();
        }

        protected void parse() {
            StringTokenizer tokens = new StringTokenizer(this.getExpression());
            this.setProperty(tokens.nextToken(TYPE_START_TOKEN));
            if (tokens.hasMoreTokens()) this.setType(tokens.nextToken());
        }

        protected String getProperty() {
            return property;
        }

        protected void setProperty(String property) {
            this.property = property;
        }

        protected String getType() {
            return type;
        }

        protected void setType(String type) {
            this.type = type;
        }

        protected boolean isStringType() {
            return "string".equalsIgnoreCase(this.getType());
        }

        protected boolean isYesNoType() {
            return "yesno".equalsIgnoreCase(this.getType());
        }

        public Object evaluate(Object model) {
            if (model == null) throw new IllegalArgumentException("model should not be null");
            Object result = null;
            if (this.getProperty().startsWith("$")) {
                result = BeanUtil.getConstant("com.erp.app.common.ProjectConstants", this.getProperty().substring(1));
            } else {
                result = BeanUtil.getPropertyDefault(model, this.getProperty());
            }
            if (result == null) return DB_NULL;
            if (this.isYesNoType()) {
                if (result instanceof Boolean) {
                    Boolean resultBoolean = (Boolean) result;
                    result = (resultBoolean.booleanValue()) ? SimpleConstants.GENERIC_YES : SimpleConstants.GENERIC_NO;
                } else {
                    String resultString = CommonUtil.getString(result);
                    result = SimpleConstants.GENERIC_YES.equalsIgnoreCase(resultString) ? SimpleConstants.GENERIC_YES : SimpleConstants.GENERIC_NO;
                }
                this.setType("string");
            }
            if (this.isStringType()) {
                return SimpleConstants.DB_STRING_QUALIFIER + CommonUtil.escapeDBString(result) + SimpleConstants.DB_STRING_QUALIFIER;
            }
            return result;
        }

    }

    protected class LiteralExpression extends SimpleExpression {

        public LiteralExpression(String expression) {
            super(expression);
        }

        public Object evaluate(Object model) {
            return this.getExpression();
        }

    }

    private final List expressions = new ArrayList();

    public BeanQuery(String query) {
        super(query);
        this.parse();
    }

    protected static final String START_TOKEN = "`";
    protected static final String END_TOKEN = "`";

    protected void parse() {
        StringTokenizer tokens = new StringTokenizer(this.getQuery());
        while (tokens.hasMoreTokens()) {
            this.getExpressions().add(new LiteralExpression(tokens.nextToken(START_TOKEN)));
            if (tokens.hasMoreTokens()) this.getExpressions().add(new BeanPropertyExpression(tokens.nextToken(END_TOKEN)));
        }
    }

    protected List getExpressions() {
        return expressions;
    }

    protected Object getModel() {
        return model;
    }

    public BeanQuery setModel(Object model) {
        this.model = model;
        return this;
    }

    public String toQuery() {
        return String.valueOf(this.evaluate(this.getModel()));
    }

    public Object evaluate(Object model) {
        StringBuffer evaluatedQuery = new StringBuffer();
        for (Iterator expressions = this.getExpressions().iterator(); expressions.hasNext();) {
            evaluatedQuery.append(((Expression) expressions.next()).evaluate(model));
        }
        return evaluatedQuery.toString();
    }

}
